home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1997 October / macformat-055.iso / mac / Shareware Plus / Developers / VideoToolbox / VideoToolboxSources / SetEntriesQuickly.c < prev    next >
Encoding:
Text File  |  1997-03-28  |  52.6 KB  |  1,389 lines  |  [TEXT/CWIE]

  1. /*
  2. SetEntriesQuickly.c
  3.  
  4. SetEntriesQuickly loads your video card’s clut as quickly as possible.
  5.  
  6. SetEntriesQuickly was written by many people, and the “final” result has not
  7. been tested on all the computers and video devices it’s meant to support. The
  8. best test is simply to run TimeVideo, which gives it a thorough workout on all
  9. of your computer’s video devices. Please send the report “TimeVideo results” to
  10. denis@xp.psych.nyu.edu, and I’ll add your test results to “Video bugs” and the
  11. following list.
  12.  
  13. 3/13/97 NEWS FLASH:
  14. Michael Zucca says, "I am currently working on color video drivers for the
  15. NetBSD/Mac community and I'm nearly done writing the driver for the IIvx/IIvi.
  16. I'll be releasing a web page explaining it along with the color kernel release
  17. in the next few days."
  18. Michael Zucca - mrz5149@rit.cs.rit.edu - http://www.rit.edu/~mrz5149/
  19.  
  20. VIDEO DEVICES CERTIFIED by TimeVideo as compatible with SetEntriesQuickly():
  21. 1. Ok:
  22. “Macintosh Display Card 8•24 GC” (.Display_Video_Apple_MDCGC)
  23. Quadra 900 “Macintosh C Built-In Video” (only tested in 1,2,4,8-bit modes)
  24. Quadra 950 “Macintosh G Built-In Video” (only tested in 1,2,4,8,16-bit modes)
  25.  
  26. 2. Ok when loading whole clut at once, but fail read-back test when loading one 
  27. entry at a time (at least on the Quadra 840av). Bug reported by Ken Alexander 
  28. <U12940@UICVM.BITNET> and Wei Xie. This problem does not occur on most Macs.
  29. Apple 8•24 "Macintosh Display Card" (.Display_Video_Apple_MDC version 272)
  30.  
  31. 3. Ok, except for visible hash (dynamic black specks) during clut loading:
  32. Apple “Mac II High-Resolution Video Card” (.Display_Video_Apple_HRVC)
  33. Apple “Toby frame buffer card” (.Display_Video_Apple_TFB version 5)
  34. Mac IIci “Macintosh II Built-In Video” (.Display_Video_Apple_RBV1)
  35.  
  36. 4. Don't work (hopefully will be fixed soon):
  37. RasterOps “ProColor 32” (.RasterOps 1.0 32XL Video Driver version 9327)
  38.  
  39. CONTRIBUTORS TO THIS LIST
  40. Ken Alexander <U12940@UICVM.BITNET> (bug in SetEntriesQuickly)
  41. Kyle Cave, cavekr@ctrvax.vanderbilt.edu (Quadra 700)
  42. Wei Xie (bug in SetEntriesQuickly for Apple 8•24 in Quadra 840AV)
  43.  
  44. Some Macintosh video drivers are poorly written; they take too long (more than a
  45. frame time) to load the clut. This is makes it impossible to do clut animation
  46. for temporal modulation etc., for which one needs to be able to reload the clut
  47. on each frame. At one time, many of us thought that the limitation was in
  48. hardware, in the RAMDAC, but we were wrong. Raynald Comptois disassembled
  49. several video drivers and wrote his own programs to load the clut quickly, and
  50. his programs manage to do it within a frame. Raynald was kind enough to share
  51. his code with me. I passed it on to Peter Lennie and Bill Haake, who polished
  52. it, making it compatible with the 68040 processor, and added support for more
  53. cards. I polished their work, made the routines self contained, adding a
  54. “device” argument to allow use in Macs that have more than one video device, and
  55. quickly figuring out all the key parameters (mode, pixelSize, DAC size, and clut
  56. size). There are now two alternative front ends: SetEntriesQuickly() for new
  57. users, and macltset() for backward compatibility with programs that used
  58. Raynald’s original routines. This modularity has increased run time only
  59. slightly, a fraction of a millisecond.
  60.  
  61. New drivers are hard to write, since they must directly address the registers of
  62. the video card, which are unique to each video card and undocumented. So the
  63. author of a driver must disassemble the original video driver and figure things
  64. out on his or her own. A lot of work.
  65.  
  66. SetEntriesQuickly is unlike the standard video drivers in the following ways:
  67. 1. SetEntriesQuickly always takes less than 2 ms to load the whole clut. 
  68. Some video drivers, e.g. for Apple’s 8•24 card, take several frames (>30 ms) to 
  69. finish loading the clut.
  70. 2A. SetEntriesQuickly does not wait for VBL, so a visible glitch may appear on
  71. the current frame, at least on some older video cards (e.g. Hi-Res, Toby, and
  72. Mac IIci). (You can prevent this glitch by calling SetEntriesQuickly only at
  73. blanking time. Use a VBL task or WaitForBlanking().) Newer cards and computers
  74. seem to have dual ported video clut memory so you can write at any point in the
  75. frame cycle without noticeable glitch, though of course you may want to synch
  76. the update for other reasons.
  77. 2B. A flag argument “waitForNextBlanking” is provided, but at present this
  78. option is only supported for the Toby video card.
  79. 3. SetEntriesQuickly ignores the gamma table, yielding the same result as using
  80. the standard video driver with an uncorrected gamma table. E.g. after calling
  81. GDUncorrectedGamma(device).
  82. 4. SetEntriesQuickly() does nothing and returns an error if the arguments
  83. specify loading of an out-of-range index. 
  84. 5. SetEntriesQuickly() does nothing and returns an error if the “count”
  85. specifies zero entries. This is contrary to the (bizarre) Apple convention that
  86. a cscSetEntries control call with a count corresponding to zero entries will result
  87. in loading of entries specified by the “value” field of the ColorSpec.
  88. 6. SetEntriesQuickly does not have immediate access to the video driver’s
  89. private tables. Therefore the first time you call SetEntriesQuickly() for a 
  90. particular device there is an extra delay of about 1 ms while some key
  91. information is ferreted out. That information is cached, so subsequent calls
  92. for the same device will be fast, spending most of their time loading the clut.
  93.  
  94. Two front ends are provided, for compatibility with two distinct traditions:
  95.  
  96. OSErr SetEntriesQuickly(GDHandle device,short start,short count,ColorSpec *table);
  97.  
  98. SetEntriesQuickly() uses the same calling convention as the VideoToolbox routine
  99. GDSetEntries() and, except for adding the GDHandle argument to specify the
  100. device, is also the same as Apple’s SetEntries() Color Manager routine,
  101. documented in Inside Macintosh V-143. Apple specifies special behavior when
  102. count==-1, but we don’t support that here and simply return with an error. I
  103. suggest that new users use SetEntriesQuickly rather that macltset. “start” is
  104. the index of the first clut entry to load, and should be greater than or equal
  105. to zero. “count” is the number of entries to load, minus 1. (Yes, “minus 1”,
  106. that’s Apple’s confusing convention.) “table” is a Colorspec array. (Each ColorSpec
  107. element is a structure consisting of a two-byte “value”, which is not used, and
  108. a 6-byte “rgb”, which, in turn is a structure of three 16-bit unsigned short
  109. ints: red, green, and blue. Apple’s convention is that the MOST SIGNIFICANT BITS
  110. of the 16-bit color values are used. It’s good practice in your programs to
  111. provide full 16-bit values, so that when you upgrade to fancier video cards with
  112. more-than-eight bit DACs your programs will benefit from the extra precision
  113. without needing any change. Returns zero if successful, nonzero if unsuccessful,
  114. i.e. illegal arguments.
  115.  
  116. short macltset(GDHandle device,short start
  117.     ,unsigned short* red,unsigned short* green,unsigned short* blue,short count1);
  118.  
  119. macltset() uses a calling convention established by Raynald Comtois, and
  120. provides backward compatibility with older programs. “red”, “green”, and “blue”
  121. are arrays of 16-bit unsigned short ints, of which the LEAST SIGNIFICANT BITS
  122. are used. “start” is the index of the first entry to change. “count1” is the
  123. number of entries to change (contrary to Apple’s convention).
  124.  
  125. Both front ends use the same general-purpose subroutine: LoadClut(), which in
  126. turn calls the hardware-specific routine appropriate to the particular video 
  127. device being used.
  128.  
  129. The useMostSignificantBits bit of the “flags” argument specifies whether to use 
  130. Apple’s convention (for users of SetEntriesQuickly) or Raynald’s convention 
  131. (for users of macltset).
  132.  
  133. At the moment all the supported video cards have 8-bit DACs, except the RasterOps
  134. ProColor 32, which has 9-bit DACs. If the useMostSignificantBits flag is true
  135. then you don’t need to worry, as the least significant bit of the 9-bit DAC
  136. simply picks up the next lower bit from your numbers, giving you a tad more
  137. precision. However, if useMostSignificantBits flag is false then, in order to
  138. use the full range of the DAC you must make all your numbers twice as big, or --
  139. cludge time! -- set the useOnly8Bits flag, to request that your 8-bit numbers be
  140. multiplied by two, allowing you to use the whole range of the DAC without
  141. changing the rest of your program, but wasting the DAC’s least significant bit
  142. by setting it permanently to zero.
  143.  
  144. SUSPENDING INTERRUPTS. If you wish, the low-level routines will suspend
  145. interrupts while loading the clut. Presumably Raynald had his reasons for
  146. implementing this, so this “feature” is enabled when you use macltset(). Peter
  147. Lennie writes, “The switch to uninterruptable processing during the write is, I
  148. think, out of the original drivers (though I’m not absolutely sure).  I imagine
  149. it’s to avoid display glitches that would result from some higher priority
  150. interrupt suspending a clut rewrite somewhere in the middle.” However, I (dgp)
  151. don’t see any advantage to suspending interrupts, and believe that there is a
  152. significant downside if you are trying to keep track of the VBL interrupts on
  153. several video cards, since suspending interrupts for 1 or 2 ms might be long
  154. enough to miss a whole VBL interval. Thus SetEntriesQuickly disables this
  155. “feature”. However, this is not a philosophical debate. We all agree that
  156. interrupts should be suspended if doing otherwise would occasionally result in a
  157. visible glitch. Does anyone know?
  158.  
  159. OSErr WaitForNextBlanking(GDHandle device);
  160.  
  161. Waits for beginning of next blanking interval. Currently this supports only the
  162. Toby and HiRes cards (Apple’s original video cards, now obsolete).
  163.  
  164. SPEED. SetEntriesQuickly() is self contained. You simply give it the GDHandle of
  165. your video device (as returned, e.g. by GetScreenDevice), and tell it what you
  166. want to do to the clut. In order to do this for you it needs to figure out a
  167. bunch of stuff about your video device. This research takes time; the first time
  168. you call it for a particular device it takes on the order of 1 ms to look up
  169. stuff. However, it saves this info in a cache, for each device, for quick
  170. retrieval on subsequent occasions. The implication is that programs that use
  171. SetEntriesQuickly ought to call it once just for practice (to get the cache
  172. loading over with) before using it in a situation where speed matters.
  173.  
  174. The coding of the LoadClut “driver” routines is a compromise between the needs
  175. of SetEntriesQuickly and macltset, which both use them. I decided not to write
  176. separate clut loading loops for the two cases (use most- vs. least-significant
  177. bits). I believe (but have not tested) that adding a register offset instead of
  178. using an autoincrement instruction incurs essentially no time penalty because
  179. the processor automatically overlaps the execution of such instructions. So I
  180. think that SetEntriesQuickly is running flat out, and don’t see any prospect of
  181. speeding it up significantly. On the other hand, I suspect that fetching the
  182. least significant byte by doing a byte access to an odd address (for macltset)
  183. does slow things down perhaps 30% (though I haven’t timed it) over doing a word
  184. access to an even address, as Raynald had originally coded it. If that speed
  185. loss is unacceptable, then one could insert an if(flags&useMostSignificantBits)
  186. statement into the relevant subroutine and write two separate loops optimized
  187. for the two cases. My guess is that the current compromise will be acceptable to
  188. all users.
  189.  
  190. A NOTE ON SPEED FROM DAVID BRAINARD:
  191. We have been looking pretty closely at video timing.  In our hands,
  192. SetEntriesQuickly does not always succeed in writing the CLUT during the
  193. vertical blank interval.  In particular, with the Apple 8-24 board on a IIfx
  194. running in the 800 by 600 mode at 75 Hz, it is a little too slow.  This causes a
  195. visible glitch at the top of the screen.  Whether it is fast enough is probably
  196. very hardware-configuration dependent, but it might be worth emphasizing in the
  197. documentation that use is caveat emptor.  You give < 2 ms as the write time,
  198. which is roughly correct, but not always fast enough. I may end up writing an
  199. assembler version to see if I can push it by 25%, which is about what I need.
  200. [However, assembly code won't be compatible with the Power PC.-dgp]
  201.  
  202. IMPROVEMENTS:
  203. It is hoped that others will add to the functionality of this routine. Please
  204. share your enhancements with others by sending them to denis@xp.psych.nyu.edu
  205. for inclusion in the VideoToolbox.
  206.  
  207. Those wishing to support new video devices should begin by buying and reading
  208. Apple’s Designing Cards and Drivers, 3rd Ed., Addison-Wesley, and then use the
  209. VideoToolbox utility GetVideoDrivers to copy all your drivers into resource
  210. files, and use ResEdit with CODE editor to peruse them. The ResEdit CODE editor
  211. is a public domain file distributed by:
  212. Ira L. Ruben
  213. Apple Computer, Inc.
  214. 20525 Mariani Ave., MS: 37-A
  215. Cupertino, Ca. 95014
  216. Ira@Apple.Com
  217. ftp://ftp.apple.com/dts/mac/tools/resedit/resedit-2-1-code-editor...
  218.  
  219. By the way, assembly code is hard to write, read, and maintain,
  220. and the speed advantage is negligible, about 10%. I suggest that
  221. all new code be written in C.
  222.  
  223. It is logical that we identify the video card by the card name,
  224. GDCardName(driver), but in fact getting the card name is very slow (1.5 ms)
  225. whereas getting the driver name is fast, GDName(driver), and would be
  226. sufficiently unique for our purposes. (E.g. the Toby and TFB video cards have
  227. the same driver, and our code works for both cards.) 
  228.  
  229. KNOWN BUGS:
  230. Has not been tested on all the video devices that are supposed to be supported.
  231. Please run the demo TimeVideo, and send the results file to denis@xp.psych.nyu.edu
  232.  
  233. Does not work with the RasterOps ProColor 32. Hopefully this will be fixed soon.
  234.  
  235. The Quadra code requires that start==0. (Apparently the same problem occurs when
  236. running the 8•24 card on a Quadra 840AV.) This could probably be figured out and
  237. fixed pretty easily if someone took the time to do so.
  238.  
  239. I recommend using the standard drivers (i.e. GDSetEntries/GDDirectSetEntries)
  240. instead of SetEntriesQuickly for the Toby and High Resolution video cards and
  241. the Mac IIci built-in video. Those standard drivers work fine, whereas for those
  242. devices SetEntriesQuickly produces visible dynamic black specks as it accesses
  243. the clut.
  244.  
  245. None of these routines wait for the vertical blanking interval before loading
  246. the clut. On older video devices--Toby, HiRes, Mac IIci--this results in visible
  247. dynamic black specks on the screen. I (dgp) consider this a bug, but, for most
  248. of these devices I don’t know how to wait for the end of frame, short of setting
  249. up an interrupt. (Just about every video card has a bit that one could monitor,
  250. but its address is usually undocumented.) Newer devices seem to be ok, because of
  251. dual-ported RAMDAC memory. Check this out on your devices by running TimeVideo.
  252.  
  253. QUADRA 660AV & 840AV
  254. Ben Singer, bens@swift.cvs.rochester.edu, writes,
  255. "I thought this information would prove useful to anyone using VideoToolbox on a
  256. 660AV or 840AV (the machines' video drivers are identical). It's possible to
  257. load the clut on 660/840AV's using the following code information:
  258.  
  259. INITIALIZATION INFO: 
  260.  
  261. slot id's for slot 0 (internal video):
  262.  
  263. quadra660 = 0x50; // quadra 660AV internal video
  264. quadra840 = 0x3D; // quadra 840AV internal video
  265.  
  266. driver name in each case is ".Display_Video_Apple_Civic"
  267. card name for 660AV is "Macintosh 3B"
  268. card name for 840AV is "Macintosh 3A"
  269.  
  270. CARDBASE INFO:
  271.  
  272. case quadra660:
  273. case quadra840:
  274.    {    
  275.     asm {
  276.         move.l 0xDD8,a0        // internal video 040 AV's
  277.         adda.l (a0),a0
  278.         move.l 164(a0),a1
  279.         move.l a1,cardBase
  280.     }
  281.     break;
  282.    }
  283.  
  284. LOADCLUT INFO:
  285.  
  286. case quadra660:
  287. case quadra840:
  288.    {
  289.     asm {    // no _SwapMMUMode; 040 AV's always in 32-bit mode
  290.         move.l cardBase,a1  
  291.         lea 0x10(a1),a1    // offsets 0x10,0x110,0x210,0x310 work
  292.         clr.l -16(a1)
  293.         move sr,-(a7)
  294.         ori #0x700,sr    // disable interrupts
  295.         subq.l #1,count // may want to take this out for VidTB
  296.         clr.l d1
  297.     @1    move.w (red)+,d1
  298.         move.b d1,(a1)
  299.         move.w (green)+,d1
  300.         move.b d1,(a1)
  301.         move.w (blue)+,d1
  302.         move.b d1,(a1)
  303.         clr.b (a1)
  304.         dbf count,@1
  305.         move (a7)+,sr    // enable interrupts
  306.     }
  307.    }
  308.    break;
  309.  
  310. I've tested this on both the 660AV and 840AV, but only in 8-bit ("256
  311. colors";count=256) mode. As with other quadra internal video code, this doesn't
  312. incorporate the "start" parameter. Thanks to Peter Lennie. Thanks also to you
  313. for GrabDriver and the tip on using the ResEdit CODE extension to examine the
  314. disassembly.
  315.  
  316. Ben Singer
  317. Postdoc, Center for Visual Science
  318. University of Rochester
  319. bens@swift.cvs.rochester.edu (Ben Singer)
  320.  
  321. RASTEROPS 8L & 24L:
  322. Rhea Eskew, eskew@neu.edu, writes "I use a RasterOps 8L board (it's probably
  323. obsolete now), which has one of those lousy drivers. However, RasterOps was nice
  324. enough to provide me with the register address and the blanking bit address, so
  325. I write directly to the hardware. The RasterOps 8L is the same as the 24L, just
  326. less memory. So I presume the register addresses are the same for the two
  327. boards. The board's base address is Fx000000, where x=Nubus slot number. The
  328. CLUT offset is FA0000. The vertical blanking bit is at offset F70000. Wait for
  329. bits 1 and 2 to both go high, then low. Ignore bit 0.
  330.      Here is the 680x0 assembly code that I use to write directly to the hardware of
  331. my RasterOps 8L board, to fill the clut. It's based on some asm supplied by
  332. RasterOps. Because I use FORTH, the opcode conventions shown below look a bit
  333. different from conventional 680x0 opcodes, but of course they do the same
  334. things. I think they could be figured out pretty easily (and I'm available if
  335. someone wants help)."
  336. hex                         \ everything's in hex
  337. FC000000 constant Cardbase    \ my card is in slot C -- the base address is
  338.                             \ Fx000000; substitute your hex slot # for x
  339. CardBase FA0000 + constant ClutBase        \ RasterOps-supplied offset
  340. CardBASE F70000    + constant VertBitOff    \ offset to vertical blanking register
  341. \ This is a FORTH code routine -- takes as stack-input parameters the listed
  342. \ numbers with VertBitOff on top of stack.
  343. \ ClutBase is base addr of rasterops board (FCA0000 for slot C)+Clutoffset (00FA0000)
  344. \ Sourceaddr is base address in RAM of the ColorSpecs (index,red,green,blue).
  345. \ count is number'of'em. (The clut data are stored as WORDS not bytes.)
  346. \ VertBitOff is register for vertical blanking (RObase+F70000)
  347. \ 'sp' stands for stack pointer = register a7
  348. \ (This version seems to work w/o glitches -- moved blanking up to top of routine.)
  349. code    R-OSetPal ( ClutBase\Sourceaddr\count\VertBitOff -- )
  350.     1  D0 MOVEQ, A05D w,    \ swappmmumode to 32bit
  351.     a4    d3    long    move,    \ save UP (a4) in d3; restore at end!
  352.     sp )+ a4    long move,    \ put vertbitoff into a4 
  353.     \ wait for blanking
  354.     begin,
  355.         a4 ()   1D 2    d1    bfextu,    \ offset is 29=$1D, bits 2 and 1 (bit 0 not used!!!)
  356.         d1    3      byte subq,    \ 3 equals 2 bits set at low end of d1
  357.     eq        until,            \ wait until it equals 3
  358.     begin,
  359.         a4 ()   1D 2    d1    bfextu,    \ offset is 29=$1D bits 2 and 1 (bit 0 not used!!!)
  360.     eq        until,            \ wait until both bits are zero
  361.     \ blanking done
  362.     sp )+ D0 long move,        \ put count to d0
  363.     sp )+ a1 long move,        \ put source into a1
  364.     sp )+ A0    long move,    \ put destination (clutbase) to a0
  365.     begin,
  366.         a1 )+ 3 a0  i) byte move,    \ index 
  367.         a1    1           long    ADDQ,    \ increment an extra byte
  368.         a1 )+ 7 a0  i) byte move,    \ red
  369.         a1    1           long    ADDQ,    \ increment an extra byte
  370.         a1 )+ 7 a0  i) byte move,    \ green
  371.         a1    1           long    ADDQ,    \ increment an extra byte
  372.         a1 )+ 7 a0  i) byte move,    \ blue
  373.         a1    1           long    ADDQ,    \ increment an extra byte
  374.         d0 1 long subq,
  375.     eq until,       
  376.     d3    a4    long move,        \ Restore UP
  377.     0  D0 MOVEQ, A05D w,    \ SwapMMUMode back
  378.     next
  379. end-code
  380.  
  381. HISTORY:
  382. 8/24/92 Original setcardbase and macltset provided by Raynald Comtois
  383. (raco@wjh12.harvard.edu) to Denis Pelli.
  384.  
  385. 10/2/92 Bill Haake added code for the RasterOps ProColor 32, which has 9-bit
  386. DACs and 9-bit entries in the lookup table.
  387.  
  388. 10/1/92 Peter Lennie added code for Quadra internal video.  No provision for
  389. changing the start position in the table, (I couldn't find any relevant
  390. disassembly) so 'start' is ignored, and you should write the whole table.
  391.     
  392. 9/30/92 Bill Haake & Peter Lennie modified the code for the 8x24 card
  393. and the 8x24GC to make it a) work properly in 32-bit mode. b) to fix a bug
  394. (feature?) of the original drivers that prevented the cards running on a Quadra.
  395. The drivers exploit 'byte-smearing' on the 68020 and 68030 (Tech Note 282). This
  396. means that one can move a byte to the lowest byte address of the data register
  397. on the card, when one actually wants to put it at address+3 (!!). The functions
  398. work for all the cards (except toby, which hasn't been tested) and on internal
  399. video in both 24 and 32 bit mode on Quadra 700/950, IIfx or ci running system
  400. 7.0.1.
  401.     
  402. 9/28/92 Peter Lennie added the function findcard.
  403.  
  404. 11/23/92 Denis Pelli (dgp) eliminated all globals because they implicitly
  405. assumed that there is only one video device. All routines now accept a GDHandle
  406. specifying which video device. Simplified the logic of GetCardBase(), minimizing
  407. the dependence on card type.
  408.  
  409. 11/25/92 dgp When USE_MSB is true, all the routines now use the most significant
  410. bits of the 16-bit elements of the user-supplied color tables. When it is false
  411. the least significant bits are used. This is mostly implemented by offseting the
  412. table pointers by one byte and only reading the desired byte. •Generalized
  413. macltset() to work with tables that have an arbitrary element spacing. This
  414. allows it to work with both with Raynald's convention of three arrays of shorts,
  415. and the Apple convention of a ColorSpec array, each element of which consists of
  416. red, green, blue, and value (which is not used). •Added alias "Toby frame buffer
  417. card" for tobycard.
  418.  
  419. 11/27/92 dgp Broke out the code for each card into separate subroutines. This
  420. allows optimal register assignment for each routine, and makes it much easier to read
  421. the THINK C disassembler output. The runtime overhead of loading and unloading
  422. the stack is negligible, and could be eliminated entirely by putting all the
  423. parameters in a structure and passing a pointer to it. •Added a flag,
  424. suspendInterrupts, to make interrupt suspension optional since it may be
  425. undesirable in some applications. (Blocking interrupts for 1 ms could cause you
  426. to miss the interrupt from a video card, especially if you are trying to keep
  427. track of interrupts on several video cards at once.)
  428.  
  429. 11/30/92 dgp Wrote TestCluts, which reads back the clut and checks all
  430. values, and used it to test SetEntriesQuickly() on Quadra 950 internal video,
  431. Mac IIci internal video, hirescard, "Toby frame buffer card", and 8•24 card at
  432. all depths, for both 24- and 32-bit addressing. Toby card was tested on 68020,
  433. 68030, and 68040 processors. •Wrote documentation. •Replaced compile-time constants
  434. USE_MSB and PRO_8BITS by runtime flags passed as arguments. •Added WaitForNextBlanking()
  435. based on code from VideoTFB.c.
  436.  
  437. 12/3/92 dgp Incorporated Peter Lennie's corrections and additions to the comments above.
  438.  
  439. 12/8/92 dgp Added missing "case" to switch in WaitForNextBlanking.
  440.  
  441. 12/13/92 dgp Changed erroneous "&d" to "%d" in a printf. Added some comments to
  442. the documentation above.
  443.             
  444. 12/15/92 dgp Now get mode from device record and leave it in standard form, 
  445. i.e. with the 0x80 bit set, and only strip off that bit when actually necessary,
  446. e.g. in LoadClutMacIIci. 
  447.  
  448. 12/30/92 dgp Make sure routines return zero when there's no error.
  449.  
  450. 2/15/93    dgp Rewrote nonworking LoadClutToby in C, and made it work. Rewrote
  451. nonworking LoadClutx824 in C, and made it work. Fixed sixteenBitMode in
  452. LoadClutQuadra. Use new SwapPriority instead of Get/SetPriority.
  453.  
  454. 2/20/93    dgp    Translated LoadClutGCx824 to C. (It was ignoring the start value.)
  455.  
  456. 3/4/93    dgp    Added macIIsi to list of supported cards, since it uses the same
  457. driver as the Mac IIci. Changed definitions of string types slightly to allow
  458. compilation of this file as a code resource. However, the assembly code
  459. uses more registers than are available to a code resource.
  460.  
  461. 4/13/93    dgp    Removed 68020 requirement by translating an indexed add in LoadMacIIci to
  462. C.
  463.  
  464. 4/17/93 dgp Added support in GetCardBase for old Mac II computers whose ROM's only 
  465. support 24-bit NuBus addressing. 
  466.  
  467. 5/18/93 SetEntriesQuickly now respects the setting of the device's gray/color mode, 
  468. and maps to gray if in the device is in gray mode and pixelSize<=8. Changed prototype
  469. of macltset to specify the red, green, and blue arrays as "unsigned short" instead of 
  470. "short".
  471.  
  472. 7/7/93    dgp    Disabled some global optimizations because THINK C 6 will
  473. crash while compiling if the Radius PowerView is present: "!gopt_induction,!gopt_loop".
  474.  
  475. 7/9/93    dgp check for 32-bit addressing capability.
  476.  
  477. 6/14/94    dgp    can32 is now computed by calling TrapAvailable(_SwapMMUMode), which 
  478. returns correct answer even on Macs with dirty ROMs.
  479.  
  480. 6/14/94    dgp    Added call to SwapMMUMode in LoadClutToby, because it seemed odd not to.
  481. LoadClutMacIIci still doesn't, because the video buffer is in memory.
  482.  
  483. 6/30/94 dgp Updated the documentation.
  484.  
  485. 7/27/94 dgp Moved the list of compatible devices from the "Video synch" document
  486. to here, at the top of the file.
  487.  
  488. 7/29/94 dgp Made use of the asm directive conditional on THINK_C, for compatibility
  489. with other compilers. Changed LoadClutx824GC() to use its C instead of its asm code. 
  490. However, I don't know if the C code has been tested. GetCardType() returns nonzero
  491. only if the card is supported by the compiled code (i.e. ProColor, Quadra, and Mac
  492. IIci and IIsi are recognized only if this file is compiled by THINK C.)
  493.  
  494. 1/13/95 dgp Added information, above, from Rhea Eskew about the RasterOps 8L.
  495.  
  496. 1/16/95 dgp Added information, above, from Ben Singer about the Quadra 840AV & 660AV.
  497. Added LoadClutQuadraAV() based on Ben's code.
  498.  
  499. 5/23/95 dgp Apple changed the prototype in the header file from SwapMMUMode(char *) to 
  500.             SwapMMUMode(signed char *). To retain compatibility with both old and new
  501.             headers, I cast the argument (void *).
  502. 8/15/95 dgp changed style of definition of internalVideoBase for compatibility with
  503.          Symantec C++. Bug reported by Bosco
  504. 2/17/97    dgp    Added SetEntriesQuicklyRecommended(), as suggested by David Brainard.
  505. 2/18/97 dhb Conditionals to get it to compile as a 68K code resource.  There are one
  506.             too few registers, so comment out register declaration for blue pointer.
  507.         dhb    Added 8-24 cards to list of recommended drivers.
  508. 3/24/97    dgp    Removed 840 and 660 (same video hardware) from list of recommended drivers,
  509.             because David Brainard reported that clut loading doesn't work quite right.
  510. */
  511. #include "VideoToolbox.h"
  512. #define _SwapMMUMode 0xA05D
  513. #define USE_ONLY_8_BITS_IN_MACLTSET 0    // 1 to use RasterOps ProColor32 as an 8-bit DAC.
  514. #if (THINK_C || THINK_CPLUS || SYMANTEC_C)
  515.     #pragma options(assign_registers,honor_register,redundant_loads,defer_adjust)
  516.     #pragma options(global_optimizer,!gopt_induction,!gopt_loop,gopt_cse,gopt_coloring)
  517. #endif
  518.  
  519. // These are the six user-callable routines:
  520. Boolean SetEntriesQuicklyRecommended(GDHandle device);
  521. OSErr WaitForNextBlanking(GDHandle device);
  522. OSErr SetEntriesQuickly(GDHandle device,short start,short count,ColorSpec *table);
  523. short macltset(GDHandle device,short start
  524.     ,unsigned short* red,unsigned short* green,unsigned short* blue,short count1);
  525. short GetCardType(GDHandle device);
  526. char *GetCardBase(GDHandle device);
  527.  
  528. /*
  529. I suggest keeping the following information private to this file. In principle
  530. you could publish these card types and use them in your programs. However, in
  531. practice, I cannot see any point in doing so. If you need to identify the card
  532. name I suggest you simply use the string returned by GDCardName(device) in
  533. GDVideo.c of the VideoToolbox. (Don't forget to call DisposPtr() when you're
  534. through with the string.) Or use GDName(device), which returns the name of the
  535. card's driver, and is much quicker. If you simply want to know whether your
  536. video card is supported by SetEntriesQuickly.c then you can simply make sure
  537. that GetCardType() returns a nonzero cardType.
  538. */
  539. struct vtype {        /* associates card name and id */
  540.     char name[40];
  541.     short id;
  542. };
  543. enum {                /* card identifiers */
  544.     tobycard = 1,
  545.     hirescard,
  546.     macIIci,
  547.     macIIsi,
  548.     x824card,
  549.     x824GCcard,
  550.     quadra700,
  551.     quadra900,
  552.     quadra950,
  553.     procolor32,
  554.     quadra660 = 0x50, /* quadra 660AV internal video */
  555.     quadra840 = 0x3D /* quadra 840AV internal video */
  556. };
  557.  
  558. static struct vtype card[] = {    // card name & id        // Original author:
  559.     {"Toby frame buffer card",                tobycard},    // Raynald Comtois
  560.     {"Display_Video_Apple_TFB",                tobycard},    //     "      "
  561.     {"Mac II High-Resolution Video Card",    hirescard},    // Raynald Comtois
  562.     {"Macintosh Display Card",                x824card},    // Raynald Comtois
  563.     {"Macintosh Display Card 8•24 GC",        x824GCcard},// Raynald Comtois
  564.     #if THINK_C    // the asm directive is supported only by THINK C
  565.         {"Macintosh II Built-In Video",            macIIci},    // Raynald Comtois
  566.         {"Macintosh A Built-In Video",            macIIsi},    //     "      "
  567.         {"Macintosh E Built-In Video",            quadra700},    // Peter Lennie
  568.         {"Macintosh C Built-In Video",            quadra900},    //     "      "
  569.         {"Macintosh G Built-In Video",            quadra950},    //     "      "
  570.         {"Macintosh 3A",                        quadra840},    // Ben Singer
  571.         {"Macintosh 3B",                        quadra660},    // Ben Singer
  572.         {"ProColor 32",                            procolor32}    // Bill Haake
  573.     #endif
  574. };
  575. /*
  576. static char driverName[][40]=        // Not used at present.
  577. {
  578.     "\p.Display_Video_Apple_TFB"    // Apple “Toby frame buffer card”
  579.     ,"\p.Display_Video_Apple_HRVC"    // Apple “Mac II High-Resolution Video Card”
  580.     ,"\p.Display_Video_Apple_MDC"    // Apple 8•24 “Macintosh Display Card”
  581.     ,"\p.Display_Video_Apple_MDCGC"    // Apple 8•24GC
  582.     ,"\p.Display_Video_Apple_RBV1"    // Mac IIci and IIsi built-in video
  583.     ,"\p.Display_Video_Apple_DAFB"    // Quadra 700, 900, 950 built-in video
  584.     ,"\p.Display_Video_Apple_Civic"    // Quadra 640AV, 840AV built-in video
  585.     ,"\p.RasterOps 1.0 32XL Video Driver"    // Radius ProColor 32
  586. };
  587. */
  588. enum {                            // Flags passed to LoadClut().
  589.     suspendInterrupts=1,
  590.     useMostSignificantBits=2,
  591.     useOnly8Bits=4,
  592.     waitForNextBlanking=8
  593. };
  594. enum{quadraNonzeroStart=111};    // value returned as error.
  595.     
  596. short LoadClut(GDHandle device,short start,short count
  597.     ,unsigned short* red,unsigned short* green,unsigned short* blue,long elementSpacing,short flags);
  598. OSErr LoadClutProColor(short start,short count,char *r,char *g,char *b
  599.     ,long elementSpacing,short mode,short pixelSize,short clutSize
  600.     ,char *cardBase,short flags);
  601. OSErr LoadClutQuadraAV(short start,short count,char *r,char *g,char *b
  602.     ,long elementSpacing,short mode,short pixelSize,short clutSize
  603.     ,char *cardBase,short flags);
  604. OSErr LoadClutQuadra(short start,short count,char *r,char *g,char *b
  605.     ,long elementSpacing,short mode,short pixelSize,short clutSize
  606.     ,char *cardBase,short flags);
  607. OSErr LoadClutMacIIci(short start,short count,char *r,char *g,char *b
  608.     ,long elementSpacing,short mode,short pixelSize,short clutSize
  609.     ,char *cardBase,short flags);
  610. OSErr LoadClutHiRes(short start,short count,char *r,char *g,char *b
  611.     ,long elementSpacing,short mode,short pixelSize,short clutSize
  612.     ,char *cardBase,short flags);
  613. OSErr LoadClutx824(short start,short count,char *r,char *g,char *b
  614.     ,long elementSpacing,short mode,short pixelSize,short clutSize
  615.     ,char *cardBase,short flags);
  616. OSErr LoadClutx824GC(short start,short count,char *r,char *g,char *b
  617.     ,long elementSpacing,short mode,short pixelSize,short clutSize
  618.     ,char *cardBase,short flags);
  619. OSErr LoadClutToby(short start,short count,char *r,char *g,char *b
  620.     ,long elementSpacing,short mode,short pixelSize,short clutSize
  621.     ,char *cardBase,short flags);
  622. /******************************************************************************/
  623. /*
  624. We recommend switching to SetEntriesQuickly only if the standard driver
  625. takes more than one frame to load the clut.
  626. All drivers will load your clut. A few cannot read the clut, but usually that doesn't matter.
  627. */
  628. Boolean SetEntriesQuicklyRecommended(GDHandle device)
  629. {
  630.     switch(GetCardType(device)){
  631.     case quadra700:
  632.     case quadra900:
  633.     case quadra950:
  634. //    case quadra660:    // same hardware as quadra840
  635. //    case quadra840:    // David Brainard reports that our clut loading doesn't work quite right.
  636.     case x824card:
  637.     case x824GCcard:
  638.         return 1;
  639.     default:
  640.         return 0;
  641.     }
  642. }
  643.  
  644.  
  645. /******************************************************************************/
  646. /*
  647. The arguments start, count, and table are the same as for the Color Manager call
  648. SetEntries(), documented in Inside Macintosh V-143. (Except that a count==-1 is
  649. considered illegal here.) Apple's ideosyncratic convention is that "count" is
  650. "zero-based", meaning that it is one less than the number of clut entries that
  651. you want to modify. "count" must be at least zero. Returns zero if successful,
  652. nonzero if unsuccessful, i.e. illegal arguments.
  653. */
  654. OSErr SetEntriesQuickly(GDHandle device,short start,short count,ColorSpec *table)
  655. {
  656.     short flags=useMostSignificantBits;
  657.     //flags+=suspendInterrupts;        // Optional, no
  658.     //flags+=waitForNextBlanking;    // Optional, no
  659.  
  660.     return LoadClut(device,start,count
  661.         ,&table[0].rgb.red,&table[0].rgb.green
  662.         ,&table[0].rgb.blue,sizeof(table[0]),flags);
  663. }
  664. /******************************************************************************/
  665. short macltset(GDHandle device,register short start
  666.     ,unsigned short* red,unsigned short* green,unsigned short* blue,short count1)
  667. {
  668.     short flags=0;
  669.     flags+=suspendInterrupts;        // Optional
  670.     #if USE_ONLY_8_BITS_IN_MACLTSET
  671.         flags+=useOnly8Bits;        // Optional
  672.     #endif
  673.     //flags+=waitForNextBlanking;    // Optional
  674.     
  675.     return LoadClut(device,start,count1-1,red,green,blue,sizeof(red[0]),flags);
  676. }
  677. /******************************************************************************/
  678. /*
  679. The first call to GetCardType for a particular device takes 1.5-3 ms, depending
  680. on your computer's speed, because it takes Apple's Slot Manager a long time to
  681. get the card name. However, GetCardType's answers are cached so subsequent calls
  682. for a previously queried device will be fast <100 µs.
  683. */
  684. short GetCardType(GDHandle device)    // returns card type, if known, or zero if not.
  685. {
  686.     register short i;
  687.     short cardType;
  688.     char *name;
  689.     static GDHandle deviceCache[MAX_SCREENS];
  690.     static short typeCache[MAX_SCREENS];
  691.     
  692.     // Do we already know the answer? Check the cache.
  693.     for(i=0;i<MAX_SCREENS;i++)if(device==deviceCache[i])return typeCache[i];
  694.     // Get card name, see if it's in our list of known cards
  695.     name=GDCardName(device);
  696.     cardType=0;
  697.     for (i=0; i<sizeof(card)/sizeof(card[0]); i++){
  698.         if(strcmp(name,card[i].name)==0){
  699.             cardType=card[i].id;
  700.             break;
  701.         }
  702.     }
  703.     DisposePtr(name);
  704.     // Save answer in cache.
  705.     for(i=0;i<MAX_SCREENS;i++)if(deviceCache[i]==0){
  706.         typeCache[i]=cardType;
  707.         deviceCache[i]=device;
  708.         break;
  709.     }
  710.     return cardType;
  711. }
  712. /******************************************************************************/
  713. //long internalVideoBase:0xDD8;    // Undocumented System global
  714. #define internalVideoBase (*((long *)0xDD8))
  715.  
  716. char *GetCardBase(GDHandle device)
  717. {
  718.     long cardBase,slot;    /* slot must be declared long */
  719.     short cardType;
  720.     
  721.     slot=GetDeviceSlot(device);
  722.     cardType=GetCardType(device);
  723.     if(slot==0){
  724.         // Built-in video, not in a NuBus slot.
  725.         switch(cardType){
  726.         case quadra660:
  727.         case quadra840:
  728.             #if 1
  729.                 // This C statement is equivalent to Ben Singer's assembly code below.
  730.                 cardBase = *(long *)(internalVideoBase + *(long *)internalVideoBase + 164);
  731.             #else
  732.                 asm {
  733.                     move.l 0xDD8,a0        /* internal video 040 AV's */
  734.                     adda.l (a0),a0
  735.                     move.l 164(a0),a1
  736.                     move.l a1,cardBase
  737.                 }
  738.             #endif
  739.             break;
  740.         default:
  741.             // E.g.: macIIci,macIIsi,quadra700,quadra900,quadra950
  742.             #if 1
  743.                 // This C is equivalent to Raynald's assembly code below.
  744.                 cardBase = *(long *)(internalVideoBase + *(long *)internalVideoBase + 56);
  745.             #else
  746.                 asm {
  747.                     move.l 0xDD8,a0        /* get card base address */
  748.                     adda.l (a0),a0
  749.                     move.l 56(a0),a1
  750.                     move.l a1,cardBase
  751.                 }
  752.             #endif
  753.             break;
  754.         }
  755.     }else{
  756.         // Video card in NuBus slot
  757.         switch(cardType){
  758.             case x824GCcard:
  759.                 cardBase = slot<<28;    // a superslot
  760.                 break;
  761.             case tobycard:
  762.             case hirescard:
  763.                 cardBase = (slot<<24) | 0xF0000000;
  764.                 cardBase+= (slot<<20);    // Support old Mac II 24-bit NuBus addressing
  765.                 break;
  766.             case procolor32:             // RasterOps
  767.             case x824card:
  768.             default:
  769.                 cardBase = (slot<<24) | 0xF0000000;
  770.                 break;
  771.         }
  772.     }
  773.     return (char *)cardBase;
  774. }
  775. /******************************************************************************/
  776. short LoadClut(GDHandle device,short start,short count
  777.     ,unsigned short* red,unsigned short* green,unsigned short* blue
  778.     ,long elementSpacing,short flags)
  779. {
  780.     char *cardBase;
  781.     short cardType=0,pixelSize,mode;
  782.     short clutSize;     // entries in the lookup table
  783.     int error,i,j;
  784.     short isGray;
  785.     unsigned short grayTable[256];
  786.     
  787.     if(device==NULL)return 1;
  788.     cardType=GetCardType(device);    // takes 1.7 ms the first time for each device.
  789.     if(cardType==0)return 1;
  790.     cardBase=GetCardBase(device);
  791.     clutSize=GDClutSize(device);
  792.     pixelSize=(**(**device).gdPMap).pixelSize;
  793.     mode=(**device).gdMode;
  794.     
  795.     // Check range.
  796.     if(start>clutSize-1 || start<0 || count+start>clutSize-1 || count<0)return 1;
  797.     
  798.     // We're going to use these RAM addresses in 32-bit mode.
  799.     red = (unsigned short *)StripAddress(red);
  800.     green = (unsigned short *)StripAddress(green);
  801.     blue = (unsigned short *)StripAddress(blue);
  802.  
  803.     if(waitForNextBlanking & flags){
  804.         WaitForNextBlanking(device);
  805.     }
  806.     
  807.     isGray=!TestDeviceAttribute(device,gdDevType);
  808.     if(isGray && pixelSize<=8){
  809.         j=elementSpacing/sizeof(*red);
  810.         for(i=0;i<=count;i++){
  811.             grayTable[i]=*red*0.30+*green*0.59+*blue*0.11;
  812.             red+=j;
  813.             green+=j;
  814.             blue+=j;
  815.         }
  816.         elementSpacing=sizeof(grayTable[0]);
  817.         red=green=blue=grayTable;
  818.     }
  819.     
  820.     // After the above setting up, actually loading 256x3 clut entries takes <2 ms.
  821.     switch (cardType) {
  822.         // I packaged the code for each case into a separate subroutine
  823.         // in order to allow the THINK C compiler to optimize each
  824.         // one independently. An important consideration is that the THINK C 5.04
  825.         // compiler disables most optimizations for any function that includes
  826.         // the "asm" directive anywhere within the function. Thus mixing C and assembly
  827.         // will result in inefficient C. No less important, the THINK C Disassemble
  828.         // command is very handy in writing fast C code, but produces an uncommented 
  829.         // listing, which is much easier to read if the separate routines are each 
  830.         // named subroutines.
  831.         case procolor32:
  832.             error=LoadClutProColor(start,count,(char *)red,(char *)green,(char *)blue,
  833.                 elementSpacing,mode,pixelSize,clutSize,cardBase,flags);
  834.             break;
  835.         case quadra660:
  836.         case quadra840:
  837.             error=LoadClutQuadraAV(start,count,(char *)red,(char *)green,(char *)blue,
  838.                 elementSpacing,mode,pixelSize,clutSize,cardBase,flags);
  839.             break;
  840.         case quadra700:
  841.         case quadra900:
  842.         case quadra950:
  843.             error=LoadClutQuadra(start,count,(char *)red,(char *)green,(char *)blue,
  844.                 elementSpacing,mode,pixelSize,clutSize,cardBase,flags);
  845.             break;
  846.         case macIIci:
  847.         case macIIsi:
  848.             error=LoadClutMacIIci(start,count,(char *)red,(char *)green,(char *)blue,
  849.                 elementSpacing,mode,pixelSize,clutSize,cardBase,flags);
  850.             break;
  851.         case hirescard:
  852.             error=LoadClutHiRes(start,count,(char *)red,(char *)green,(char *)blue,
  853.                 elementSpacing,mode,pixelSize,clutSize,cardBase,flags);
  854.             break;
  855.         case x824card:
  856.             error=LoadClutx824(start,count,(char *)red,(char *)green,(char *)blue,
  857.                 elementSpacing,mode,pixelSize,clutSize,cardBase,flags);
  858.             break;
  859.         case x824GCcard:
  860.             error=LoadClutx824GC(start,count,(char *)red,(char *)green,(char *)blue,
  861.                 elementSpacing,mode,pixelSize,clutSize,cardBase,flags);
  862.             break;
  863.         case tobycard:
  864.             error=LoadClutToby(start,count,(char *)red,(char *)green,(char *)blue,
  865.                 elementSpacing,mode,pixelSize,clutSize,cardBase,flags);
  866.             break;
  867.     }
  868.     return error;
  869. }
  870. /******************************************************************************/
  871. #if MATLAB && THINK_C
  872. OSErr LoadClutProColor(short start,register short count
  873.     ,register char *red,register char *green,/* register */ char *blue
  874.     ,register long elementSpacing
  875.     ,short mode,short pixelSize,short clutSize,char *cardBase,short flags)
  876. #else
  877. OSErr LoadClutProColor(short start,register short count
  878.     ,register char *red,register char *green,register char *blue
  879.     ,register long elementSpacing
  880.     ,short mode,short pixelSize,short clutSize,char *cardBase,short flags)
  881. #endif
  882. {
  883. #if THINK_C
  884.     signed char mmuMode=true32b;
  885.     char priority=7;
  886.     register long bitShift;
  887.     static Boolean can32,firstTime=1;
  888.  
  889.     if(firstTime){
  890.         can32=TrapAvailable(_SwapMMUMode);
  891.         firstTime=0;
  892.     }
  893.     if(useMostSignificantBits & flags){
  894.         bitShift=9;
  895.     }else{
  896.         if(useOnly8Bits & flags) bitShift=1;
  897.         else bitShift=0;
  898.     }
  899.     if(suspendInterrupts & flags)SwapPriority(&priority);
  900.     if(can32)SwapMMUMode((void *)&mmuMode);
  901.     asm {
  902.         move.l cardBase,a1        /* get card base address */
  903.         adda.l    #0xf60000,a1    /* offset to control registers */        
  904.     @9    move.w    start,2(a1)        /* Set the index on the card */
  905.         move.w (red),d1
  906.         add.l elementSpacing,red
  907.         lsl.w bitShift,d1
  908.         move.w d1,14(a1)
  909.         move.w (green),d1
  910.         add.l elementSpacing,green
  911.         lsl.w bitShift,d1
  912.         move.w d1,14(a1)
  913.         move.w (blue),d1
  914.         add.l elementSpacing,blue
  915.         lsl.w bitShift,d1
  916.         move.w d1,14(a1)
  917.         addq.w    #1,start        /* Point to next entry in table */
  918.         dbf count,@9
  919.     }
  920.     if(can32)SwapMMUMode((void *)&mmuMode);
  921.     if(suspendInterrupts & flags)SwapPriority(&priority);
  922.     return 0;
  923. #else
  924.     start;count;red;green;blue;elementSpacing;mode;pixelSize;clutSize;cardBase;flags;    // prevent "unused argument" warning
  925.     return 1;
  926. #endif
  927. }
  928. /*
  929. Ben Singer writes, 
  930. "I've tested this on both the 660AV and 840AV, but only in 8-bit ("256
  931. colors";count=256) mode. As with other quadra internal video code, this doesn't
  932. incorporate the "start" parameter. Thanks to Peter Lennie. Thanks also to you
  933. for GrabDriver and the tip on using the ResEdit CODE extension to examine the
  934. disassembly."
  935. dgp adds,
  936. I modified Ben's code to work within the conventions of SetEntriesQuickly. In
  937. particular Ben was using the low byte of the color spec, but Apple suggests
  938. using the high byte. 
  939. */
  940. #if MATLAB && THINK_C
  941. OSErr LoadClutQuadraAV(short start,register short count
  942.     ,register char *red,register char *green,/* register */ char *blue
  943.     ,register long elementSpacing
  944.     ,short mode,short pixelSize,short clutSize,char *cardBase,short flags)
  945. #else
  946. OSErr LoadClutQuadraAV(short start,register short count
  947.     ,register char *red,register char *green,register char *blue
  948.     ,register long elementSpacing
  949.     ,short mode,short pixelSize,short clutSize,char *cardBase,short flags)
  950. #endif
  951. {
  952.     if(start!=0)return quadraNonzeroStart;    // code assumes start==0
  953.     if(pixelSize<8)return 1;                // code doesn't work for small pixels
  954.     #if THINK_C
  955.         asm {    // no _SwapMMUMode; 040 AV's always in 32-bit mode
  956.             move.l cardBase,a1  
  957.             lea 0x10(a1),a1    // offsets 0x10,0x110,0x210,0x310 work
  958.             clr.l -16(a1)
  959.             move sr,-(a7)
  960.             ori #0x700,sr    // disable interrupts
  961.             clr.l d1
  962.         @1    move.b (red),(a1)
  963.             add.l elementSpacing,red
  964.             move.b (green),(a1)
  965.             add.l elementSpacing,green
  966.             move.b (blue),(a1)
  967.             add.l elementSpacing,blue
  968.             clr.b (a1)
  969.             dbf count,@1
  970.             move (a7)+,sr    // enable interrupts
  971.         }
  972.     #else
  973.         // prevent "unused argument" warning
  974.         count; red; green; blue; elementSpacing; mode; pixelSize; clutSize; cardBase; flags;
  975.         return 1;
  976.     #endif
  977. }
  978. #if MATLAB && THINK_C
  979. OSErr LoadClutQuadra(short start,register short count
  980.     ,register char *red,register char *green,/* register */ char *blue
  981.     ,register long elementSpacing
  982.     ,short mode,short pixelSize,short clutSize,char *cardBase,short flags)
  983. #else
  984. OSErr LoadClutQuadra(short start,register short count
  985.     ,register char *red,register char *green,register char *blue
  986.     ,register long elementSpacing
  987.     ,short mode,short pixelSize,short clutSize,char *cardBase,short flags)
  988. #endif
  989. {
  990. #if THINK_C
  991.     signed char mmuMode=true32b;
  992.     char priority=7;
  993.     static Boolean can32,firstTime=1;
  994.  
  995.     if(firstTime){
  996.         can32=TrapAvailable(_SwapMMUMode);
  997.         firstTime=0;
  998.     }
  999.     if(start!=0){
  1000.         //printf("LoadClutQuadra: start must be zero\n");
  1001.         return quadraNonzeroStart;
  1002.     }
  1003.     if(!(useMostSignificantBits & flags)){
  1004.         // Point to less significant byte of word.
  1005.         red++;
  1006.         green++;
  1007.         blue++;
  1008.     }
  1009.     if(suspendInterrupts & flags)SwapPriority(&priority);
  1010.     if(can32)SwapMMUMode((void *)&mmuMode);
  1011.     if(mode!=sixteenBitMode)asm{
  1012.         move.l cardBase,a1
  1013.         lea 0x210(a1), a1
  1014.         clr.l -16(a1)
  1015.     @4    move.b (red),d1
  1016.         add.l elementSpacing,red
  1017.         move.l d1,(a1)
  1018.         move.b (green),d1
  1019.         add.l elementSpacing,green
  1020.         move.l d1,(a1)
  1021.         move.b (blue),d1
  1022.         add.l elementSpacing,blue
  1023.         move.l d1,(a1)
  1024.         dbf count,@4
  1025.     }else asm{
  1026.     // In sixteenBitMode the clut addressing is weird.
  1027.     // I arrived at the following solution by trial and error.
  1028.     // It's a kludge, but is still fast enough. dgp.
  1029.         move.l cardBase,a1
  1030.         lea 0x210(a1), a1
  1031.         clr.l -16(a1)
  1032.     @44    move.b (red),d1
  1033.         move.l d1,(a1)
  1034.         move.b (green),d1
  1035.         move.l d1,(a1)
  1036.         move.b (blue),d1
  1037.         move.l d1,(a1)
  1038.  
  1039.         move.b (red),d1
  1040.         move.l d1,(a1)
  1041.         move.b (green),d1
  1042.         move.l d1,(a1)
  1043.         move.b (blue),d1
  1044.         move.l d1,(a1)
  1045.  
  1046.         move.b (red),d1
  1047.         move.l d1,(a1)
  1048.         move.b (green),d1
  1049.         move.l d1,(a1)
  1050.         move.b (blue),d1
  1051.         move.l d1,(a1)
  1052.  
  1053.         move.b (red),d1
  1054.         move.l d1,(a1)
  1055.         move.b (green),d1
  1056.         move.l d1,(a1)
  1057.         move.b (blue),d1
  1058.         move.l d1,(a1)
  1059.  
  1060.         move.b (red),d1
  1061.         move.l d1,(a1)
  1062.         move.b (green),d1
  1063.         move.l d1,(a1)
  1064.         move.b (blue),d1
  1065.         move.l d1,(a1)
  1066.  
  1067.         move.b (red),d1
  1068.         move.l d1,(a1)
  1069.         move.b (green),d1
  1070.         move.l d1,(a1)
  1071.         move.b (blue),d1
  1072.         move.l d1,(a1)
  1073.  
  1074.         move.b (red),d1
  1075.         move.l d1,(a1)
  1076.         move.b (green),d1
  1077.         move.l d1,(a1)
  1078.         move.b (blue),d1
  1079.         move.l d1,(a1)
  1080.  
  1081.         move.b (red),d1
  1082.         add.l elementSpacing,red
  1083.         move.l d1,(a1)
  1084.         move.b (green),d1
  1085.         add.l elementSpacing,green
  1086.         move.l d1,(a1)
  1087.         move.b (blue),d1
  1088.         add.l elementSpacing,blue
  1089.         move.l d1,(a1)
  1090.  
  1091.         dbf count,@44
  1092.     }
  1093.     if(can32)SwapMMUMode((void *)&mmuMode);
  1094.     if(suspendInterrupts & flags)SwapPriority(&priority);
  1095.     return 0;
  1096. #else
  1097.     // prevent "unused argument" warning
  1098.     start;count;red;green;blue;elementSpacing;mode;pixelSize;clutSize;cardBase;flags;
  1099.     return 1;
  1100. #endif
  1101. }
  1102. #if MATLAB && THINK_C
  1103. OSErr LoadClutMacIIci(register short start,register short count
  1104.     ,register char *red,register char *green,/* register */ char *blue
  1105.     ,register long elementSpacing
  1106.     ,short mode,short pixelSize,short clutSize,char *cardBase,short flags)
  1107. #else
  1108. OSErr LoadClutMacIIci(register short start,register short count
  1109.     ,register char *red,register char *green,register char *blue
  1110.     ,register long elementSpacing
  1111.     ,short mode,short pixelSize,short clutSize,char *cardBase,short flags)
  1112. #endif
  1113. {
  1114. #if THINK_C
  1115.     static char realstartindex[] = {0xFE, 0xFC, 0xF0, 0x00,0,0,0};
  1116.     char priority=7;
  1117.     static Boolean can32,firstTime=1;
  1118.  
  1119.     if(firstTime){
  1120.         can32=TrapAvailable(_SwapMMUMode);
  1121.         firstTime=0;
  1122.     }
  1123.     if(!(useMostSignificantBits & flags)){
  1124.         // Point to less significant byte of word.
  1125.         red++;
  1126.         green++;
  1127.         blue++;
  1128.     }
  1129.     if(suspendInterrupts & flags)SwapPriority(&priority);
  1130.     mode&=7;
  1131.     start+=realstartindex[mode];
  1132.     asm {
  1133.         move.w mode,d1
  1134.         move.l cardBase,a0        // get card base address
  1135.         move.l a0,a1
  1136. //        move.b #255,8(a0)        // not necessary
  1137.         move.b start,(a0)
  1138.         addq.l #4,a1
  1139.     @3    move.b (red),d1
  1140.         add.l elementSpacing,red
  1141.         move.b d1,(a1)
  1142.         move.b (green),d1
  1143.         add.l elementSpacing,green
  1144.         move.b d1,(a1)
  1145.         move.b (blue),d1
  1146.         add.l elementSpacing,blue
  1147.         move.b d1,(a1)
  1148.         dbf count,@3
  1149.     }
  1150.     if(suspendInterrupts & flags)SwapPriority(&priority);
  1151.     return 0;
  1152. #else
  1153.     start;count;red;green;blue;elementSpacing;mode;pixelSize;clutSize;cardBase;flags;    // prevent "unused argument" warning
  1154.     return 1;
  1155. #endif
  1156. }
  1157. // High resolution video card
  1158. //#define HRVCBase            0x80000
  1159. #define HRVCClutAddrReg        0x940E0
  1160. #define HRVCClutWDataReg    0x940E4
  1161. //#define HRVCClutRDataReg    0x94054
  1162. OSErr LoadClutHiRes(short start,register short count
  1163.     ,register char *red,register char *green,register char *blue
  1164.     ,register long elementSpacing
  1165.     ,short mode,short pixelSize,short clutSize,char *cardBase,short flags)
  1166. {
  1167.     char *bytePtr;
  1168.     signed char mmuMode=true32b;
  1169.     char priority=7;
  1170.     static Boolean can32,firstTime=1;
  1171.  
  1172.     mode;pixelSize;    // prevent "unused argument" warning
  1173.     if(firstTime){
  1174.         can32=TrapAvailable(_SwapMMUMode);
  1175.         firstTime=0;
  1176.     }
  1177.     if(!(useMostSignificantBits & flags)){
  1178.         // Point to less significant byte of word.
  1179.         red++;
  1180.         green++;
  1181.         blue++;
  1182.     }
  1183.     if(suspendInterrupts & flags)SwapPriority(&priority);
  1184.     if(can32)SwapMMUMode((void *)&mmuMode);
  1185.     red+=count*elementSpacing;
  1186.     green+=count*elementSpacing;
  1187.     blue+=count*elementSpacing;
  1188.     // We'll start with clut entry start+count, and work
  1189.     // down from there to clut entry start. The clut address
  1190.     // register counts down automatically.
  1191.     *(cardBase+HRVCClutAddrReg)=~(clutSize-1-start-count);
  1192.     bytePtr=cardBase+HRVCClutWDataReg;
  1193.     // This is the key loop. 
  1194.     // This C code is only about 10% slower than the original assembly code.
  1195.     elementSpacing= -elementSpacing;
  1196.     do{
  1197.         *bytePtr=~ *red;
  1198.         red+=elementSpacing;
  1199.         *bytePtr=~ *green;
  1200.         green+=elementSpacing;
  1201.         *bytePtr=~ *blue;
  1202.         blue+=elementSpacing;
  1203.     }while(--count>=0);
  1204.     if(can32)SwapMMUMode((void *)&mmuMode);
  1205.     if(suspendInterrupts & flags)SwapPriority(&priority);
  1206.     return 0;
  1207. }
  1208. // Macintosh display card (8•24)
  1209. //#define MDCVideoBase        0xA00
  1210. #define MDCClutAddrReg        0x200200
  1211. #define MDCClutDataReg        0x200204
  1212. OSErr LoadClutx824(short start,register short count
  1213.     ,register char *red,register char *green,register char *blue
  1214.     ,register long elementSpacing
  1215.     ,short mode,short pixelSize,short clutSize,char *cardBase,short flags)
  1216. {
  1217.     signed char mmuMode=true32b;
  1218.     char priority=7;
  1219.     register char *clut;
  1220.     char *clutIndex;
  1221.     static Boolean can32,firstTime=1;
  1222.  
  1223.     mode;pixelSize;clutSize;    // prevent "unused argument" warning
  1224.     if(firstTime){
  1225.         can32=TrapAvailable(_SwapMMUMode);
  1226.         firstTime=0;
  1227.     }
  1228.     if(!(useMostSignificantBits & flags)){
  1229.         // Point to less significant byte of word.
  1230.         red++;
  1231.         green++;
  1232.         blue++;
  1233.     }
  1234.     if(suspendInterrupts & flags)SwapPriority(&priority);
  1235.     if(can32)SwapMMUMode((void *)&mmuMode);
  1236.     clut=cardBase+MDCClutDataReg+3;
  1237.     clutIndex=cardBase+MDCClutAddrReg;
  1238.     *clutIndex=start;
  1239.     for(;count>=0;count--){
  1240.         *clut=*red;
  1241.         red+=elementSpacing;
  1242.         *clut=*green;
  1243.         green+=elementSpacing;
  1244.         *clut=*blue;
  1245.         blue+=elementSpacing;
  1246.     }
  1247.     if(can32)SwapMMUMode((void *)&mmuMode);
  1248.     if(suspendInterrupts & flags)SwapPriority(&priority);
  1249.     return 0;
  1250. }
  1251. // Macintosh display card 8•24 GC
  1252. #define MDCgcClutAddrReg    0x6C00000
  1253. #define MDCgcClutDataReg    0x6C00004
  1254. OSErr LoadClutx824GC(short start,register short count
  1255.     ,register char *red,register char *green,register char *blue
  1256.     ,register long elementSpacing
  1257.     ,short mode,short pixelSize,short clutSize,char *cardBase,short flags)
  1258. {
  1259.     signed char mmuMode=true32b;
  1260.     char priority=7;
  1261.     register long *clut;
  1262.     char *clutIndex;
  1263.     static Boolean can32,firstTime=1;
  1264.  
  1265.     mode;pixelSize;clutSize;    // prevent "unused argument" warning
  1266.     if(firstTime){
  1267.         can32=TrapAvailable(_SwapMMUMode);
  1268.         firstTime=0;
  1269.     }
  1270.     if(!(useMostSignificantBits & flags)){
  1271.         // Point to less significant byte of word.
  1272.         red++;
  1273.         green++;
  1274.         blue++;
  1275.     }
  1276.     if(suspendInterrupts & flags)SwapPriority(&priority);
  1277.     if(can32)SwapMMUMode((void *)&mmuMode);
  1278.     clutIndex=cardBase+MDCgcClutAddrReg;
  1279.     *clutIndex=start;
  1280.     #if 1
  1281.         clut=(long *)(cardBase+MDCgcClutDataReg);
  1282.         for(;count>=0;count--){
  1283.             *clut=(long)(*red)<<24;
  1284.             red+=elementSpacing;
  1285.             *clut=(long)(*green)<<24;
  1286.             green+=elementSpacing;
  1287.             *clut=(long)(*blue)<<24;
  1288.             blue+=elementSpacing;
  1289.         }
  1290.     #else
  1291.         asm {
  1292.             move.l cardBase,a1
  1293.             add.l #MDCgcClutDataReg,a1
  1294.         @8    move.b (red),d1
  1295.             add.l    elementSpacing,red
  1296.             ror.l    #8,d1
  1297.             move.l d1,(a1)
  1298.             move.b (green),d1
  1299.             add.l    elementSpacing,green
  1300.             ror.l    #8,d1
  1301.             move.l d1,(a1)
  1302.             move.b (blue),d1
  1303.             add.l elementSpacing,blue
  1304.             ror.l    #8,d1
  1305.             move.l d1,(a1)
  1306.             dbf count,@8
  1307.         }
  1308.     #endif
  1309.     if(can32)SwapMMUMode((void *)&mmuMode);
  1310.     if(suspendInterrupts & flags)SwapPriority(&priority);
  1311.     return 0;
  1312. }
  1313. // Toby frame buffer
  1314. //#define    TFBBase            0x80000
  1315. //#define TFBBufMid            0x80008
  1316. //#define TFBBufLow            0x8000C
  1317. //#define    TFBIBase        0x8fffc
  1318. #define TFBClutWDataReg        0x90018
  1319. //#define TFBClutRDataReg    0x90028
  1320. #define TFBClutAddrReg        0x9001C
  1321. #define    TFBReadVSync        0xD0000
  1322. //#define    TFBReadVInt        0xD0004
  1323. //#define    TFBReadIntlc    0xD0008
  1324. //#define    TFBVIntEnable    0xA0000
  1325. //#define    TFBVIntDisable    0xA0004
  1326. OSErr LoadClutToby(short start,register short count
  1327.     ,register char *red,register char *green,register char *blue
  1328.     ,register long elementSpacing
  1329.     ,short mode,short pixelSize,short clutSize,char *cardBase,short flags)
  1330. {
  1331.     register long index;
  1332.     signed char mmuMode=true32b;
  1333.     char priority=7;
  1334.     register char *clut,*clutIndex;
  1335.     short shift;
  1336.     static Boolean can32,firstTime=1;
  1337.  
  1338.     mode;clutSize;    // prevent "unused argument" warning
  1339.     if(firstTime){
  1340.         can32=TrapAvailable(_SwapMMUMode);
  1341.         firstTime=0;
  1342.     }
  1343.     if(!(useMostSignificantBits & flags)){
  1344.         // Point to less significant byte of word.
  1345.         red++;
  1346.         green++;
  1347.         blue++;
  1348.     }
  1349.     index=(count+1)*elementSpacing;
  1350.     red+=index;
  1351.     green+=index;
  1352.     blue+=index;
  1353.     shift=8-pixelSize;
  1354.     index=start+count+1;
  1355.     clut=cardBase+TFBClutWDataReg;
  1356.     clutIndex=cardBase+TFBClutAddrReg;
  1357.     if(suspendInterrupts & flags)SwapPriority(&priority);
  1358.     if(can32)SwapMMUMode((void *)&mmuMode);
  1359.     for(;count>=0;count--,index--){
  1360.         *clutIndex=(index<<shift)-1;
  1361.         red-=elementSpacing;
  1362.         *clut=~*red;
  1363.         green-=elementSpacing;
  1364.         *clut=~*green;
  1365.         blue-=elementSpacing;
  1366.         *clut=~*blue;
  1367.     }
  1368.     if(can32)SwapMMUMode((void *)&mmuMode);
  1369.     if(suspendInterrupts & flags)SwapPriority(&priority);
  1370.     return 0;
  1371. }
  1372.  
  1373. OSErr WaitForNextBlanking(GDHandle device)
  1374. // WaitForNextBlanking waits for the beginning of the next vertical blanking interval.
  1375. // Returns 0 if successful, or 1 if device is not supported.
  1376. {
  1377.     register long *blankingPtr;
  1378.  
  1379.     switch(GetCardType(device)){
  1380.     case tobycard:
  1381.         blankingPtr = (long *) ((char *)GetCardBase(device) + TFBReadVSync);
  1382.         while (*blankingPtr & 1L) ;    // if we're already blanking, wait till end.
  1383.         while (!(*blankingPtr & 1L)) ; // wait until beginning of blanking interval.
  1384.         return 0;
  1385.     default:
  1386.         return 1;
  1387.     }
  1388. }
  1389.